package com.shenzhenai.nebigview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.Scroller;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.IOException;
import java.io.InputStream;

/**
 * @author huangzehui
 * @date 28/6/2025 下午5:02 周六
 * @description 手写微博巨图加载解决方案
 * https://www.bilibili.com/video/BV1aK4y1Y71n?spm_id_from=333.788.videopod.episodes&vd_source=e07cc8cc94eb22bd76722071b03cd199&p=194
 * <p>
 * <p>
 * 第1步：设置bigView需要的成员变量
 * 第2步，设置图片
 * 第3步，测量
 * 第4步，画出具体的内容
 * 第5步，处理点击事件
 * 第6步，手按下去，处理事件
 * 第7步，处理滑动事件
 */
public class NeBigView extends View {

    private Rect mRect;
    private BitmapFactory.Options mOptions;
    private GestureDetector mGestureDetector;
    private Scroller mScroller;
    private int mImageWidth;
    private int mImageHeight;
    private BitmapRegionDecoder mDecoder;
    private int mViewWidth;
    private int mViewHeight;
    private float mScale;
    private Bitmap mBitmap;
    private ScaleGestureDetector mScaleGestureDetector;
    private float originalScale;

    public NeBigView(Context context) {
        this(context, null);
    }

    public NeBigView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public NeBigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public NeBigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        init(context);
    }

    // 第3步，测量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mViewWidth = getMeasuredWidth();
        mViewHeight = getMeasuredHeight();


        // 得到图片的宽度，就能根据view的宽度计算缩放因子
//        mScale = mViewWidth / (float) mImageWidth;

        // 确定图片的加载区域（还没处理缩放的）
//        mRect.left = 0;
//        mRect.top = 0;
//        mRect.right = mImageWidth;
//        mRect.bottom = (int) (mViewHeight / mScale);

        // 确定图片的加载区域（处理缩放的写法）
        mRect.left = 0;
        mRect.top = 0;
        mRect.right = Math.min(mImageWidth, mViewWidth);
        mRect.bottom = Math.min(mImageHeight, mViewHeight);

        // 再定义一个缩放因子
        originalScale = mViewWidth / (float) mImageWidth;
        mScale = originalScale;
    }

    // 第4步，画出具体的内容
    @Override
    protected void onDraw(@NonNull Canvas canvas) {
        super.onDraw(canvas);
        if (mDecoder == null) {
            return;
        }
        /*
        复用内存
        复用bitmap必须跟即将解码的bitmap尺寸一样
         */
        mOptions.inBitmap = mBitmap;
        mBitmap = mDecoder.decodeRegion(mRect, mOptions);
        Matrix matrix = new Matrix();
//        matrix.setScale(mScale, mScale);
        matrix.setScale(mViewWidth / (float) mRect.width(), mViewWidth / (float) mRect.width());

        canvas.drawBitmap(mBitmap, matrix, null);
    }

    // 处理滚动结果
    @Override
    public void computeScroll() {
//        super.computeScroll();
        if (mScroller.isFinished()) {
            return;
        }
        if (mScroller.computeScrollOffset()) {
            mRect.top = mScroller.getCurrY();
            mRect.bottom = mRect.top + (int) (mViewHeight / mScale);
            invalidate();
        }
    }

    private void init(Context context) {
        // 第1步：设置bigView需要的成员变量
        mRect = new Rect();
        // 内存复用
        mOptions = new BitmapFactory.Options();
        // 手势识别
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {

            // 第6步，手按下去，处理事件
            @Override
            public boolean onDown(@NonNull MotionEvent e) {
                // 如果移动没有停止，就强制停止
                if (!mScroller.isFinished()) {
                    mScroller.forceFinished(true);
                }

                return true;
            }

            @Override
            public void onShowPress(@NonNull MotionEvent e) {

            }

            @Override
            public boolean onSingleTapUp(@NonNull MotionEvent e) {
                return false;
            }

            /**
             * 第7步，处理滑动事件
             * @param e1 开始事件，手指按下去，获取坐标
             * @param e2 当前滑动事件
             * @param distanceX X轴滚动的距离
             * @param distanceY Y轴滚动的距离
             * @return
             */
            @Override
            public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) {
                // 上下移动的时候，mRect需要改变显示区域
                mRect.offset((int) distanceX, (int) distanceY);

                // 移动时，处理到达顶部和底部的情况
                if (mRect.bottom > mImageHeight) {
                    mRect.bottom = mImageHeight;
                    mRect.top = mImageHeight - (int) (mViewHeight / mScale);
                }

                if (mRect.top < 0) {
                    mRect.top = 0;
                    mRect.bottom = (int) (mViewHeight / mScale);
                }

                if (mRect.right > mImageWidth) {
                    mRect.left = mImageWidth - (int) (mViewWidth / mScale);
                    mRect.right = mImageWidth;
                }

                if (mRect.left < 0) {
                    mRect.left = 0;
                    mRect.right = (int) (mViewWidth / mScale);
                }

                invalidate();
                return false;
            }

            @Override
            public void onLongPress(@NonNull MotionEvent e) {

            }

            /**
             * 处理惯性问题
             * @param e1 The first down motion event that started the fling. A {@code null} event
             *           indicates an incomplete event stream or error state.
             * @param e2 The move motion event that triggered the current onFling.
             * @param velocityX The velocity of this fling measured in pixels per second
             *              along the x axis.
             * @param velocityY The velocity of this fling measured in pixels per second
             *              along the y axis.
             * @return
             */
            @Override
            public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) {
                mScroller.fling(mRect.left, mRect.top, (int) -velocityX, (int) -velocityY, 0, mImageWidth - (int) (mViewWidth / mScale),
                        0, (int) (mImageHeight - (mViewHeight / mScale)));
                return false;
            }


            @Override
            public boolean onDoubleTap(@NonNull MotionEvent e) {
                // 双击事件
                if (mScale < originalScale) {
                    mScale = originalScale;
                } else if (mScale > originalScale * 3) {
                    mScale = originalScale * 3;
                }
                mRect.right = mRect.left + (int) (mViewWidth / mScale);
                mRect.bottom = mRect.top + (int) (mViewHeight / mScale);
                // 移动时，处理到达顶部和底部的情况
                if (mRect.bottom > mImageHeight) {
                    mRect.bottom = mImageHeight;
                    mRect.top = mImageHeight - (int) (mViewHeight / mScale);
                }

                if (mRect.top < 0) {
                    mRect.top = 0;
                    mRect.bottom = (int) (mViewHeight / mScale);
                }

                if (mRect.right > mImageWidth) {
                    mRect.left = mImageWidth - (int) (mViewWidth / mScale);
                    mRect.right = mImageWidth;
                }

                if (mRect.left < 0) {
                    mRect.left = 0;
                    mRect.right = (int) (mViewWidth / mScale);
                }

                invalidate();
                return true;
            }

        });

        // 滚动类
        mScroller = new Scroller(context);

        // 缩放手势识别
        mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() {

            // 处理缩放的回调事件
            @Override
            public boolean onScale(@NonNull ScaleGestureDetector detector) {
                float scale = mScale;
                scale += detector.getScaleFactor() - 1;
                if (scale < originalScale) {
                    scale = originalScale;
                } else if (scale > originalScale * 5) {
                    scale = originalScale * 5;
                }
                mRect.right = mRect.left + (int) (mViewWidth / scale);
                mRect.bottom = mRect.top + (int) (mViewHeight / scale);
                mScale = scale;
                invalidate();
                return true;
            }

        });

        // 设置触摸监听事件
        setOnTouchListener(new OnTouchListener() {

            // 第5步，处理点击事件
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // 直接将事件传递给手势事件处理
                mGestureDetector.onTouchEvent(event);

                mScaleGestureDetector.onTouchEvent(event);
                return true;
            }
        });
    }


    // 第2步，设置图片
    public void setImage(InputStream image) {
        // 获取图片的信息，不能将整张图片加载进内存
        mOptions.inJustDecodeBounds = true;

        BitmapFactory.decodeStream(image, null, mOptions);

        mImageWidth = mOptions.outWidth;
        mImageHeight = mOptions.outHeight;

        // 开启复用
        mOptions.inMutable = true;
        // 设置格式
        mOptions.inPreferredConfig = Bitmap.Config.RGB_565;

        mOptions.inJustDecodeBounds = false;

        // 创建一个区域解码器
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            try {
                mDecoder = BitmapRegionDecoder.newInstance(image);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            try {
                mDecoder = BitmapRegionDecoder.newInstance(image, false);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}
